//--------------------------------------------------------------------------------------
// File: StereovisionProject.fx
//
// The effect file for the StereovisionProject sample.  
//--------------------------------------------------------------------------------------


//--------------------------------------------------------------------------------------
// Global variables
//--------------------------------------------------------------------------------------
texture g_ImageTexture;             // Color texture for mesh
texture g_GaussianKernel;           // kernel for gaussian blur
int		g_ImageSizeX;				// width of the image in pixels
int		g_ImageSizeY;				// height of the image in pixels

float	g_Threshold;				// threshold parameter for edge detection algorithm

float4x4 g_mWorldViewProjection;    // World * View * Projection matrix

float   g_multiply;		
float   g_multiply_Harris;

int	g_ThomasShiResponse;
int	g_CMPResponse;
int	g_HarrisResponse;

float	g_LocalMaxThreshold;
//--------------------------------------------------------------------------------------
// Texture samplers
//--------------------------------------------------------------------------------------
sampler ImageTextureSampler = 
sampler_state
{
    Texture = <g_ImageTexture>;
    AddressU  = Wrap;
    AddressV  = Wrap;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

sampler GaussianKernelSampler = 
sampler_state
{
    Texture = <g_GaussianKernel>;
    AddressU  = Wrap;
    AddressV  = Wrap;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};
//--------------------------------------------------------------------------------------
// Vertex shader output structure
//--------------------------------------------------------------------------------------
struct VS_OUTPUT
{
    float4 Position   : POSITION;   // vertex position 
    float2 TextureUV  : TEXCOORD0;  // vertex texture coords 
};


//--------------------------------------------------------------------------------------
// Simply passing through vertex shader
//--------------------------------------------------------------------------------------
VS_OUTPUT RenderSceneVS( float4 vPos : POSITION, 
                         float3 vNormal : NORMAL,
                         float2 vTexCoord0 : TEXCOORD0 )
{
    VS_OUTPUT Output;
    
    // Transform the position from object space to homogeneous projection space
    Output.Position = mul(vPos, g_mWorldViewProjection);
    Output.TextureUV = vTexCoord0; 
    
    return Output;    
}


//--------------------------------------------------------------------------------------
// Pixel shader output structures
//--------------------------------------------------------------------------------------
struct PS_OUTPUT
{
    float4 RGBColor : COLOR0;  // Pixel color    
};

struct PS_OUTPUT_RT1
{
    float4 RGBColor : COLOR1;  // Pixel color    
};

struct PS_OUTPUT_MRT
{
    float4 RGBColor0 : COLOR0;  // Pixel color    
    float4 RGBColor1 : COLOR1;  // Pixel color    
};

//--------------------------------------------------------------------------------------
// Create black & white gaussian blob
//--------------------------------------------------------------------------------------
PS_OUTPUT CreateGaussianKernelPS(VS_OUTPUT In)
{

	const float sigma = 0.2;
	const float PI = 3.1415;
	const float center_Pos = 0.5;
	const float s = 2.0*pow(sigma, 2.0);
	const float q = 1.0/(PI*s);
	
	float result = q * exp(	
							-(	  pow(In.TextureUV.x - center_Pos, 2.0) 
								+ pow(In.TextureUV.y - center_Pos, 2.0)
							  )/s
						   );

	PS_OUTPUT Output;
	Output.RGBColor.r = Output.RGBColor.g = Output.RGBColor.b = result;
	Output.RGBColor.a = 1.0;
	return Output;
}

//--------------------------------------------------------------------------------------
// Performing gaussian blur 
// based on pre-generated Kernel, based on gaussian function,
// which is placed in GaussianKernelSampler
//--------------------------------------------------------------------------------------
PS_OUTPUT GaussianBlurPS( VS_OUTPUT In ) 
{ 
    PS_OUTPUT Output;

	const float2 texOffset = float2(1.0/g_ImageSizeX, 1.0/g_ImageSizeY);
	const int halfKernelSize = 8;
	const float kernelOffset = 0.0625;  // == ( 1.0 / (2 * halfKernelSize ))
	const float2 centerPos = float2(0.5, 0.5);

	
	
	float3 currentColor, totalColor = (0.0, 0.0, 0.0);
	float filterCoefs, filterTotal = 0.0;
	for (int w = - halfKernelSize; w <= halfKernelSize; w++)
	{
		for (int h = -halfKernelSize; h <= halfKernelSize;h++)
		{
			// extract filter coefs
			filterCoefs = tex2D(GaussianKernelSampler, centerPos + float2(float(w), float(h)) * kernelOffset).x;
			filterTotal += filterCoefs;
		
			// extract color
			currentColor = tex2D(ImageTextureSampler, In.TextureUV + float2(float(w), float(h)) * texOffset).xyz;
			totalColor += currentColor * filterCoefs;
		}
	}
	totalColor /= filterTotal;
	
	// 	r = blurred image, g = unchanged input image
	Output.RGBColor = float4(totalColor.r, tex2D(ImageTextureSampler, In.TextureUV).r, 1.0, 1.0);
    return Output;
}

//--------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------
PS_OUTPUT HessianCornerDetection( VS_OUTPUT In ) 
{ 
    PS_OUTPUT Output;

	// pixel-sized offset
	const float2 texelSize = 1.0 / float2(g_ImageSizeX, g_ImageSizeY);


    float tl = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2(-1, -1)).x);   // top left
    float  l = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2(-1,  0)).x);   // left
    float bl = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2(-1,  1)).x);   // bottom left
    float  t = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 0, -1)).x);   // top
    float  b = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 0,  1)).x);   // bottom
    float tr = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 1, -1)).x);   // top right
    float  r = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 1,  0)).x);   // right
    float br = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 1,  1)).x);   // bottom right
	float  c = abs(tex2D (ImageTextureSampler, In.TextureUV).x);								// center
 
    //------------------------------
    // Hessian partial derivatives
    //------------------------------     
    // Compute Ixx using mask:
    //            0  0  0  
    //            1 -2  1
    //            0  0  0
    float Ixx = l + -2*c + r;
    //
    // Compute Iyy using mask:
    //            0  1  0 
    //            0 -2  0
    //            0  1  0
    float Iyy = t - 2*c + b;
    //
    // Compute Ixy using mask:
    //        -0.25  0  0.25 
    //            0  0  0
    //         0.25  0 -0.25
    float Ixy = -0.25*tl + 0.25*tr + 0.25*bl - 0.25*br;
    
    //------------------------------
    // Compute eigen values of Hessian
    // prachj1_2008dipl.pdf, page 8 & CPO-W04-v01-50pr.pdf, page 55
    // http://en.wikipedia.org/wiki/Eigen
    // det(A - eigen*I) = 0
    // 
    // Equation in this case is:
	//
	//		|	Ixx-eigen	Ixy			|
	//	det	|	Ixy			Iyy - eigen	| = (Ixx - eigen)(Iyy - eigen) - (Ixy)^2
	//									  = Ixx*Iyy -Ixx*eigen - Iyy*eigen + eigen^2 - Ixy^2
	//
	//	>>>>>>>>>>>>>>>>>>>>>>>>		  = eigen^2 - (Ixx + Iyy)*eigen - Ixy^2 + Ixx*Iyy		<<<<<<<<<<<<<<<<<<<
	//
	//	delta = b^2 - 4ac
	//	delta = (Ixx+Iyy)^2 - 4*(Ixy^2 + Ixx*Iyy) 
	//		  = Ixx^2 + 2*Ixx*Iyy + Iyy^2   -  4*Ixy^2 - 4*Ixx*Iyy 
	//		  = Ixx^2 + Iyy^2 - 4*Ixy^2 - 2*Ixx*Iyy 
	//		  = Ixx*Ixx + Iyy*Iyy - 4*Ixy*Ixy - Ixx*Iyy - Ixx*Iyy
	//		  = Ixx*(Ixx - Iyy)	+ Iyy*(Iyy - Iyy) - 4*Ixy*Ixy
	//
	//	eigen = (-b +- sqrt(delta)) / 2a
    //------------------------------    
    float delta = Ixx*(Ixx - Iyy) + Iyy*(Iyy - Iyy) - 4*Ixy*Ixy;
    float sqrdelta = sqrt(delta);
    float eigen1 = (Ixx + Iyy + sqrdelta) / 2;
    float eigen2 = (Ixx + Iyy - sqrdelta) / 2;
    
    
    //------------------------------    
	// found a corner
    //------------------------------    
	float color;
    
    //------------------------------    
    // Thomas Shi response
    //------------------------------    
    if ( 1 == g_ThomasShiResponse )
		color = g_multiply*min(eigen1, eigen2);
    
    //------------------------------    
    // CMP response
    //------------------------------    
    if ( 1 == g_CMPResponse )
		color = g_multiply*max(eigen1, eigen2);
    
    //------------------------------    
    // Harris response function
    // R = det(A) - k * (trace(A))^2
    // det(A) = eigen1 * eigen2
    // trace(A) = eigen1 + eigen2
    // 0.04 < k < 0.06
    //------------------------------    
    if ( 1 == g_HarrisResponse )
    {
		float trace = eigen1 + eigen2;
		color = g_multiply*(eigen1*eigen2 - g_multiply_Harris*trace*trace);    
	}
    
    // r = corner color, g = unchanged input image, b = blurred input image
	Output.RGBColor = float4(color, tex2D (ImageTextureSampler, In.TextureUV).g, tex2D (ImageTextureSampler, In.TextureUV).r, 1.0);
	
    return Output;
}

// Multiple Render Targets (MRT)
// we save the image to a file on disk 
// and show the image on screen
PS_OUTPUT_MRT LocalMaximumResponsePS( VS_OUTPUT In )
{
	PS_OUTPUT_MRT Output;
	Output.RGBColor0 = float4(0.0, 0.0, 0.0, 1.0);
	Output.RGBColor1 = float4(0.0, 0.0, 0.0, 1.0);
	
	// pixel-sized offset
	const float2 texelSize = 1.0 / float2(g_ImageSizeX, g_ImageSizeY);

    float tl = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2(-1, -1)).x);   // top left
    float  l = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2(-1,  0)).x);   // left
    float bl = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2(-1,  1)).x);   // bottom left
    float  t = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 0, -1)).x);   // top
    float  b = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 0,  1)).x);   // bottom
    float tr = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 1, -1)).x);   // top right
    float  r = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 1,  0)).x);   // right
    float br = abs(tex2D (ImageTextureSampler, In.TextureUV + texelSize * float2( 1,  1)).x);   // bottom right
	float  c = abs(tex2D (ImageTextureSampler, In.TextureUV).x);								// center
	
	//----------------------------
	// checking if the response's maximal
	// in the pixel's neighbourhood
	// and larger than the threshold
	//----------------------------	
	Output.RGBColor0 = float4(0.0, tex2D (ImageTextureSampler, In.TextureUV).g, 0.0, 1.0);
	Output.RGBColor1.r = Output.RGBColor1.g = Output.RGBColor1.b = tex2D (ImageTextureSampler, In.TextureUV).b;
	if ((c > tl) && (c > l) && (c > bl) && (c > t) && (c > b) && (c > tr) && (c > r) && (c > br) && (c > g_LocalMaxThreshold))
	{
		Output.RGBColor0.r = 1.0; 
		Output.RGBColor1 = float4(1.0, 0.0, 0.0, 1.0);
	}	
	
	return Output;
}


//--------------------------------------------------------------------------------------
// Shaders passing input to output
//--------------------------------------------------------------------------------------
PS_OUTPUT ShowImagePS( VS_OUTPUT In ) 
{
	PS_OUTPUT Output;
	Output.RGBColor = tex2D( ImageTextureSampler, In.TextureUV );
	return Output;
}

// multiple render targets
PS_OUTPUT_MRT ShowImageRT1PS( VS_OUTPUT In ) 
{
	PS_OUTPUT_MRT Output;
	Output.RGBColor0 = Output.RGBColor1 = tex2D( ImageTextureSampler, In.TextureUV );
	return Output;
}

//--------------------------------------------------------------------------------------
// Techniques
//--------------------------------------------------------------------------------------
technique CornerDetector
{
    pass P0
    {          
        VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader  = compile ps_3_0 GaussianBlurPS(); 
    }
    pass P1
    {          
		VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader  = compile ps_3_0 HessianCornerDetection(); 
    }
    pass P2
    {          
        VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader  = compile ps_3_0 LocalMaximumResponsePS();         
    }
}

technique CreateGaussianKernel
{
    pass P0
    {          
        VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader  = compile ps_3_0 CreateGaussianKernelPS(); 
    }
}
technique ShowImage
{
    pass P0
    {          
        VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader  = compile ps_3_0 ShowImagePS(); 
    }    
    pass P1
    {          
        VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader  = compile ps_3_0 ShowImagePS(); 
    }    
    pass P2
    {          
        VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader  = compile ps_3_0 ShowImageRT1PS(); 
    }            
}